// SVG namespace
var svgNS = "http://www.w3.org/2000/svg";
// the SVG document handler
var documentSVG = null;
// will store the root <g> element that groups all chart elements
var chartGroup = null;
// how often to request new data from server?
var updateInterval = 1000; 
// coordinates (in pixels) used to translate the chart
var x = 50, y = 50;
// chart's dimension (in pixels) 
var height = 300, width = 500;
// chart's axis origin 
var xt1 = 0, yt1 = 0;
// chart's axis maximum values
var xt2 = 50, yt2 = 100;
// number of horizontal and vertical axis divisions
var xDivisions = 10, yDivisions = 10;
// default text width and height for initial display (recalculated 
// afterwards)
var defaultTextWidth = 30, defaultTextHeight = 20; 
// will retain references to the chart units for recalculating positions
var xIndexes = new Array(xDivisions + 1);
var yIndexes = new Array(yDivisions + 1);
// will store the text node that displays the selected chart node 
var currentNodeInfo;
// retains the latest values generated by server 
var lastX = -1, lastY = -1;
// shared svg elements 
var chartGroup, dataGroup, dataPath;

 
// initializes chart 
function init(evt) 
{
  /**** Prepare the group that will contain all chart data  ****/
  // obtain SVG document handler
  documentSVG = evt.target.ownerDocument;
  // create the <g> element that groups all chart elements 
  chartGroup = documentSVG.createElementNS(svgNS, "g");
  chartGroup.setAttribute("transform", "translate(" + x + " " + y + ")");
  
  /**** Prepare the group that will store the Y and Y axis and numbers ****/
  axisGroup = documentSVG.createElementNS(svgNS, "g");
  // create the X axis line as a <path> element
  axisPath = documentSVG.createElementNS(svgNS, "path");
  // the axis lines will be black, 2 pixels wide
  axisPath.setAttribute("stroke", "black"); 
  axisPath.setAttribute("stroke-width", "2");

  /**** Create the division lines for the X and Y axis  ****/  
  // create the path definition text for the X axis division lines
  pathText = "M 0 " + height; 
  // adds divisions to the X axis (differently for last division)
  for (var i = 0; i <= xDivisions; i++) 
    pathText += "l 0 5 l 0 -5 " +
      ((i == xDivisions) ? "" : ("l " + width/xDivisions + " 0"));
  // create the path definition text for the Y axis division lines  
  pathText += "M 0 " + height;
  // adds one division to the Y axis (differently for last division)
  for (var i = 0; i <= yDivisions; i++)
    pathText += "l -5 0 l 5 0 " + 
      ((i == yDivisions) ? "" : ("l 0 -" + height / yDivisions)); 
  // add the path definition (the <d> attribute) to the path
  axisPath.setAttribute("d", pathText); 
  // add the path to the axis group
  axisGroup.appendChild(axisPath);

  /**** Create the text nodes for the X and Y axis  ****/
  // adds text nodes for the X axis
  for (var i = 0; i <= xDivisions; i++)   
  {
    // creates the <text> node for the division
    t = documentSVG.createElementNS(svgNS, "text");
    // stores the node for future reference 
    xIndexes[i] = t;
    // creates the text for the <text> node
    t.appendChild(documentSVG.createTextNode(
                   (xt1 + i * ((xt2 - xt1) / xDivisions)).toFixed(1)));
    // sets the X and Y attributes for the <text> node 
    t.setAttribute("x", i * width / xDivisions - defaultTextWidth / 2);
    t.setAttribute("y", height + 30 + defaultTextHeight); 
    // when the graph first loads, we want the text nodes invisible
    t.setAttribute("stroke", "white"); 
    // add the <text> node to the axis group
    axisGroup.appendChild(t);
  }
  // adds text nodes for the Y axis
  for (var i = 0; i <= yDivisions; i++)
  {
    // creates the <text> node for the division
    t = documentSVG.createElementNS(svgNS, "text");
    // stores the node for future reference 
    yIndexes[i] = t;
    // creates the text for the <text> node
    t.appendChild(documentSVG.createTextNode(
                   (yt1 + i * ((yt2 - yt1) / yDivisions)).toFixed(1))); 
 
    // sets the X and Y attributes for the <text> node
    t.setAttribute("x", -30 -defaultTextWidth);
    t.setAttribute("y", height - i * height / yDivisions 
                        + defaultTextHeight / 2); 
    // when the graph first loads, we want the text nodes invisible
    t.setAttribute("stroke", "white");  
    // add the <text> node to the axis group
    axisGroup.appendChild(t);
  }
  
  // add the axis group to the chart
  chartGroup.appendChild(axisGroup);

  /**** Prepare the <path> element that will draw chart's data ****/ 
  dataPath = documentSVG.createElementNS(svgNS, "path"); 
  dataPath.setAttribute("stroke", "black");
  dataPath.setAttribute("stroke-width", "1");
  dataPath.setAttribute("fill", "none");
  // add the data path to the chart group 
  chartGroup.appendChild(dataPath);
  
  /**** Final initialization steps ****/
  // add the chart group to the SVG document 
  documentSVG.documentElement.appendChild(chartGroup);
  // this is needed to correctly display text nodes in Firefox  
  setTimeout("refreshXYIndexes()", 500);
  // initiate repetitive server requests
  setTimeout("updateChart()", updateInterval);
}

// this function redraws the text for the axis units and makes it visible
// (this is required to correctly position the text in Firefox)
function refreshXYIndexes()
{
  // redraw text nodes on the X axis
  for (var i = 0; i <= xDivisions; i++)
    if (typeof xIndexes[i].getBBox != "undefined")
      try 
      {
        textWidth = xIndexes[i].getBBox().width;
        textHeight = xIndexes[i].getBBox().height;
        xIndexes[i].setAttribute("x", i*width/xDivisions - textWidth/2);
        xIndexes[i].setAttribute("y", height + 10 + textHeight);
        xIndexes[i].setAttribute("stroke", "black");
      } 
      catch(e) {}
  // redraw text nodes on the Y axis
  for (var i = 0; i <= yDivisions; i++)
    if (typeof yIndexes[i].getBBox != "undefined") 
      try 
      {
        twidth = yIndexes[i].getBBox().width;
        theight = yIndexes[i].getBBox().height;
        yIndexes[i].setAttribute("y", height-i*height/yDivisions
                                                          +theight/2);
        yIndexes[i].setAttribute("x", -10 -twidth);
        yIndexes[i].setAttribute("stroke", "black");
      } 
      catch(e) {} 
}

 
// called when mouse hovers over chart node to display its coordinates 
function createPointInfo(x, y, whereX, whereY) 
{
  // make sure you don't display more coordinates at the same time
  if (currentNodeInfo) removePointInfo();
  // create text node
  currentNodeInfo = documentSVG.createElementNS(svgNS, "text");
  currentNodeInfo.appendChild(documentSVG.createTextNode("("+x+","+y+")"));
  // set coordinates
  currentNodeInfo.setAttribute("x", whereX.toFixed(1));
  currentNodeInfo.setAttribute("y", whereY - 10);
  // add the node to the group
  chartGroup.appendChild(currentNodeInfo);
}
  
// removes the text node that displays chart node coordinates       
function removePointInfo()
{
  chartGroup.removeChild(currentNodeInfo);
  currentNodeInfo = null;
}
  
// draws a new point on the graph
function addPoint(X, Y) 
{
  // save these values for future reference
  lastX = X;
  lastY = Y;
  // start over (reload page) after the last value was generated
  if (X == xt2)
    window.location.reload(false);
  // calculate the coordinates of the new node
  coordX = (X - xt1) * (width / (xt2 - xt1));  
  coordY = height - (Y - yt1) * (height / (yt2 - yt1));
  // draw the node on the chart as a blue filled circle
  var circle = documentSVG.createElementNS(svgNS, "circle"); 
  circle.setAttribute("cx", coordX); // X position
  circle.setAttribute("cy", coordY); // Y position
  circle.setAttribute("r", 3); // radius
  circle.setAttribute("fill", "blue"); // color
  circle.setAttribute("onmouseover", 
                     "createPointInfo(" + X + "," + 
                     Y + "," + coordX + "," + coordY + ")");
  circle.setAttribute("onmouseout", "removePointInfo()");
  chartGroup.appendChild(circle);
  // add a new line to the new node on the graph
  current = dataPath.getAttribute("d"); // current path definition
  // update path definition
  if (!current || current == "")
    dataPath.setAttribute("d", " M " + coordX + " " + coordY);  
  else
    dataPath.setAttribute("d", current + " L " + coordX + " " + coordY);
}

// initiates asynchronous request to retrieve new chart data
function updateChart()
{
  // builds the query string
  param = "?lastX=" + lastX + ((lastY != -1) ? "&lastY=" + lastY : "");
  // make the request through either AJAX
  if (window.getURL)
    // Supported by Adobe's SVG Viewer and Apache Batik
    getURL("svg_chart.php" + param, handleResults);
  else
 
    // Supported by Mozilla, implemented in ajaxRequest.js
    ajaxRequest("svg_chart.php" + param, handleResults);
}

// callback function that reads data received from server   
function handleResults(data) 
{
  // get the response data
  if (window.getURL)
    responseText = data.content;
  else
    responseText = data; 
  // split the pair to obtain the X and Y coordinates
  var newCoords = responseText.split(",");
  // draw a new node at these coordinates
  addPoint(newCoords[0], newCoords[1]);  
  // restart sequence
  setTimeout("updateChart()", updateInterval)	
}
